package io;

import io.CubeRW;
import model.*;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;

/**
 * Osztály, amely segítségével tudunk fájlokba kocka kirakóst írni. 
 *
 */
public class CubeRW {
	/**A fájlrendszer útvonala. Az összes puzzle a files folderben található. 
	 * Ebben van 4 újabb folder: 3, 4, 5, 6 - a méreteknek megfelelően. 
	 * Mindegyikben ezek közül található egy 0.txt nevő fájl. Ennek első sorában van egy N szám,
	 * amely jelzi, hogy eddig hány kockakirakós szerepel az adott méretűből a folderben.
	 * Továbbá N folder van itten (1-től N-ig számozva), és mindegyikben egy-egy puzzle darabjai 
	 * illetve egy újabb 0.txt fájl, mely első sorában szintén egy szám: hány darabból áll a puzzle.
	 */
	private String directory = "../files/";
	/**Új folder létrehozása, mikor kiiratni szeretnénk puzzlet.*/
	private File folder;
	
	/**
	 * Konstruktor.
	 */
	public CubeRW() {
		
	}
		
	/**
	 * Folder létrehozása.
	 * @param path - ez az útvonal, ahova a foldert szeretnénk létrehozni.
	 * 			   - tartalmazza a foldernevet is.
	 */
	private void createFolder(String path) {
		folder = new File(path);
		folder.mkdir();
	}
	
	/**
	 * A fájl első sorában szereplő szám kiolvasása, növelése eggyel és ennek elmentése a fájlba.
	 * @param file - a fájl neve, melyben a számot módosítani szeretnénk.
	 * @return - a fájlban szereplő szám eggyel növelve.
	 */
	private int increaseNumberInFile(String file) {
		try {
			int n;
			// fájl olvasásához szükséges readerek
			FileReader input = new FileReader(file);
			BufferedReader in = new BufferedReader(input);
			// beolvassuk a fájl első sorát 
			String s  = in.readLine();
			// számmá alakítjuk az első sort
			n = Integer.parseInt(s);			//elso sor a kocka merete			
			in.close();		
			// szám novelése
			++n;
			// fájl írásához szükséges writerek
			FileWriter output = new FileWriter(file);
			BufferedWriter out = new BufferedWriter(output);
			// beírjuk a növelt számot az első sorba
			out.write(Integer.toString(n));	
			out.close();
			return n;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}	
		return -1;
	}
	
	/**
	 * A fájl első sorában szereplő szám kiolvasása.
	 * @param file - a fájl neve, melyből a számot olvassuk.
	 * @return - a fájlban szereplő szám.
	
	 */
	private int readNumberFromFile(String file) {
		// ugyanúgy működik mint az előbbi metódus csak itt nem noveljük a számot
		try {
			int n;
			FileReader input = new FileReader(file);
			BufferedReader in = new BufferedReader(input);
			String s  = in.readLine();
			n = Integer.parseInt(s);			//elso sor a kocka merete			
			in.close();		
			return n;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}	
		return -1;
	}
	
	/**
	 * Szám beírása a fájl első sorába.
	 * @param file - a fájl neve, melybe a számot írni szeretnénk.
	 * @param n - a szám, melyet a fájlba írni szeretnénk.
	 */
	public void writeNumberToFile(String file, int n) {
		try {
			// A fájl írásához szükséges writerek.
			FileWriter output = new FileWriter(file);
			BufferedWriter out = new BufferedWriter(output);
			out.write(Integer.toString(n));	//elso sor kocka merete + ures sor
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}		
	}
	
	/**
	 * Metódus, mely kiiratja a kocka kirakós darabjait és a megoldást
	 * a megfelelő helyre.
	 * @param cubeList - a kirakandó kocka darabjait tartalmazza.
	 * @param solution - a kirakott kocka darabjait tartalmazza.
	 */
	public void writePuzzle(Matrix3D [] cubeList, Matrix3D [] solution) {
		// lekérjük a puzzle darabjainak számát
		int n = cubeList.length;
		// lekérjük a kockák méretét - ez alapján döntjük el, hogy a files folderben hova kerül a puzzle
		int size = cubeList[0].getSize();
		// a fájlrendszer útvonalát állítjuk a megfelelő méret folderére.
		String dir = directory + Integer.toString(size) + "/";
		// növeljük ezen a helyen a 0.txt fájlban szereplő számot, 
		//	  jelezve ezáltal, hogy eggyel több kocka kirakós került a fájlrendszerbe
		int m = this.increaseNumberInFile(dir + "0.txt");
		if (m == -1)
			return;
		// állítjuk az útvonalat az új kiiratási helyre
		dir = dir + Integer.toString(m);
		// létrahozzuk az újabb puzzle számára a foldert
		this.createFolder(dir);
		// beírjuk az aktuális puzzle darabjainak számát a 0.txt fájlba
		this.writeNumberToFile(dir + "/0.txt", n);
		// egyenként kiiratjuk a kirakós darabjait
		for (int i=0; i < n; ++i) {
			cubeList[i].writeCubeToFile(dir + "/g" +  (i+1) + ".txt");
		}
		
		if (size!=6) {  // ha n nem 6
		// egyenként kiiratjuk a megoldott kirakós darabjait
			for (int i=0; i < n; ++i) {
				solution[i].writeCubeToFile(dir + "/s" +  (i+1) + ".txt");
			}
		}
	}
	
	/**
	 * Megadott méretű kocka kirakós kiolvasása véletlenszerűen.
	 * @param size - a kocka mérete, amilyent olvasni szeretnénk.
	 * @return - lista amely tartalmazza a kocka kirakós darabjait.
	 */
	public Matrix3D [] readRandomPuzzle(int size) {
		// állítjuk a fájlrendszer őtvonalát a megadott méretú folderre
		String dir = directory + Integer.toString(size) + "/";
		// kiolvassuk a 0.txt fájlból, hogy hány puzzle van eddig
		int n = this.readNumberFromFile(dir + "0.txt");
		// véletlenszerűen kiválasztunk egyet ezek közül
		Random r = new Random();
		int m = r.nextInt(n) + 1;
		// állítjuk az őtvonalat a megfelelő folderre
		dir = dir + Integer.toString(m) + "/";
		// kiolvassuk, hogy ebben a folderben a puzzle hány darabból áll
		n = this.readNumberFromFile(dir + "0.txt");
		// létrehozunk egy tömböt a darabok tárolására
		Matrix3D [] a = new Matrix3D[n];
		// megyünk végig az állományokon és kiolvassuk a kocka kirakós darabjait
		for (int i=0; i<n; ++i) {
			a[i] = new Matrix3D(size);
			try {
				// létrahozzuk az i. darab tárolására az objektumot
				a[i] = new Matrix3D(size);
				// kiolvassuk fájlból a darabot
				a[i].readCubeFromFile(dir + "g" + (i+1) + ".txt");
			} catch (MatrixException e) {
				e.printStackTrace();
			}
		}
		return a;
	}
	
	/**
	 * Megadott útvonal alapján kocka kirakós olvasunk a fájlrendszerből.
	 * @param path - az útvonal ahonnakón olvasunk
	 * @param number - a kirakandó kocka ennyi darabjainak száma
	 * @param size - a kirakandó kocka mérete
	 * @return - lista, mely tartalmazza a kockát
	 */
	public Matrix3D [] readPuzzleFromFile(String path, int number, int size) {
		int n = number;
		// létrehozzuk a tömböt a kockák tárolására
		Matrix3D [] a = new Matrix3D[n];
		// egyenként kiolvassuk a darabokat
		for (int i=0; i<n; ++i) {
			a[i] = new Matrix3D(size);
			try {
				a[i] = new Matrix3D(size);
				a[i].readCubeFromFile(path + "/" + (i+1) + ".txt");
			} catch (MatrixException e) {
				e.printStackTrace();
			}
		}
		return a;
	}
	
//end of class
}
